//-----------------------------------------------------------------------------
//
// CodeXS - Copyright(c) Businessware Architects cc
// Contact: Willem J Fourie
// E-mail : willemf@bware.biz
//
// This file is part of CodeXS.
//
// CodeXS is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License (GPL) as 
// published by the Free Software Foundation version 2 of the License.
//
// CodeXS is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with CodeXS; if not, write to:
//
// Free Software Foundation, Inc.
// 59 Temple Place
// Suite 330
// Boston
// MA  02111-1307  USA
//
//-----------------------------------------------------------------------------

using System;
using System.Reflection;
using System.CodeDom;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Collections;
using CodeXS.Schema;
using CodeXS.Schema.CodeBuilder;
using CodeXS.Schema.CodeModifier;

//#if NET_2_0
using System.Xml.Serialization.Advanced;
//#endif // NET_2_0

namespace CodeXS.Schema.StandardCodeModifier
{
	#region  1. ReferencesConstruct
	public class ReferencesConstruct : ICodeModifier
	{
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			CodeGenerator.CodeNamespace.Imports.Add(
				new CodeNamespaceImport("System"));
			CodeGenerator.CodeNamespace.Imports.Add(
				new CodeNamespaceImport("System.IO"));
			CodeGenerator.CodeNamespace.Imports.Add(
				new CodeNamespaceImport("System.Text"));
			CodeGenerator.CodeNamespace.Imports.Add(
				new CodeNamespaceImport("System.Collections"));
			CodeGenerator.CodeNamespace.Imports.Add(
				new CodeNamespaceImport("System.Xml"));
			CodeGenerator.CodeNamespace.Imports.Add(
				new CodeNamespaceImport("System.Xml.Schema"));
			CodeGenerator.CodeNamespace.Imports.Add(
				new CodeNamespaceImport("System.Xml.Serialization"));
            //CodeGenerator.CodeNamespace.Imports.Add(
            //    new CodeNamespaceImport("FrXSDLib"));
        }
		public bool Enabled
		{
			get { return true; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion
	}
	#endregion  1. ReferencesConstruct
	#region  2. SerializationAttributes
	public class SerializationAttributes : ICodeModifier
	{
		#region Implementation
		private string _nameSpaceArgument = "Namespace";
		private CodeAttributeArgument CreateFormArgument(
			XmlSchemaForm SchemaForm)
		{
			string s = "XmlSchemaForm." +
				Enum.GetName(typeof(XmlSchemaForm), SchemaForm);
			CodeAttributeArgument arg = 
				CodeSerializationAttribute.CreateArgument(
					"Form", s, true);
			return arg;
		}
		void SetTypeForm(CodeTypeMember Member, 
			XmlSchemaForm ElementForm, XmlSchemaForm AttributeForm,
			CodeAttributeArgument NamespaceArg,
			string TargetNamespace, bool QualifyIfTargetNamespace)
		{
			CodeAttributeArgument elArg = 
				this.CreateFormArgument(ElementForm);
			CodeAttributeArgument atArg = 
				this.CreateFormArgument(AttributeForm);
			CodeAttributeDeclarationCollection elAttrs = 
				CodeSerializationAttribute.GetAttribute(
					Member, typeof(XmlElementAttribute));
			CodeAttributeArgument unqualArg = 
				this.CreateFormArgument(XmlSchemaForm.Unqualified);
			foreach(CodeAttributeDeclaration elAttr in elAttrs)
			{
				if(ElementForm != XmlSchemaForm.None)
				{
					CodeSerializationAttribute.SetArgument(
						elAttr, elArg);
				}
			}
			CodeAttributeDeclarationCollection atAttrs = 
				CodeSerializationAttribute.GetAttribute(
					Member, typeof(XmlAttributeAttribute));
			foreach(CodeAttributeDeclaration atAttr in atAttrs)
			{
				// Create the correct 'Form' argument.
				// Get the 'Namespace' argument in the attribute.
				CodeAttributeArgument nsAttr = 
					CodeSerializationAttribute.GetArgument(
						atAttr, _nameSpaceArgument);
				// If no 'Namespace' and 'Form' is specified,
				// use the passed in namespace argument.
				if(AttributeForm != XmlSchemaForm.None)
				{
					if( (nsAttr == null) &&
						(NamespaceArg != null) )
					{
						nsAttr = NamespaceArg;
						CodeSerializationAttribute.SetArgument(
							atAttr, nsAttr);
					}
					if(nsAttr != null)
					{
						CodePrimitiveExpression expr = (CodePrimitiveExpression)
							nsAttr.Value;
						if( (expr.Value.ToString() == TargetNamespace) &&
							(QualifyIfTargetNamespace == false) )
						{
							CodeSerializationAttribute.SetArgument(
								atAttr, unqualArg);
						}
						else
						{
							CodeSerializationAttribute.SetArgument(
								atAttr, atArg);
						}
					}
				}
			}
		}
		void RemoveNamespaceAttributeWhenUnqualified(
			CodeTypeMember Member, 
			XmlSchemaForm AttributeForm)
		{
			CodeAttributeDeclarationCollection arAttrs = 
				CodeSerializationAttribute.GetAttribute(
				Member, typeof(XmlArrayItemAttribute));
			foreach(CodeAttributeDeclaration arAttr in arAttrs)
			{
				// check for unqualified form and remove namespace
				// attribute.
				if(AttributeForm == XmlSchemaForm.Unqualified)
				{
					// Get the 'Namespace' argument in the attribute.
					CodeAttributeArgument nsAttr = 
						CodeSerializationAttribute.GetArgument(
						arAttr, _nameSpaceArgument);
					if(nsAttr != null)
					{
						arAttr.Arguments.Remove(nsAttr);
					}
				}
			}
		}
		void SetAttributeTypeOf(CodeTypeMember Member)
		{
			CodeMemberField field = Member as CodeMemberField;
			int dim = CodeModifier.ReferenceType.GetArrayDimension(field.Type);
			if(dim > 0)
			{
				CodeAttributeDeclarationCollection arAttrs = 
					CodeSerializationAttribute.GetAttribute(
					Member, typeof(XmlArrayItemAttribute));
				foreach(CodeAttributeDeclaration arAttr in arAttrs)
				{
					foreach(CodeAttributeArgument arg in arAttr.Arguments)
					{
						CodeExpression expr = arg.Value;
						if( (expr != null) && (expr is CodeTypeOfExpression))
						{
							// CHANGE: 2006-06-29:	 Version 0.57 Beta
							// Ignore if field base type is 'System.Objec'.
							if (field.Type.BaseType != "System.Object")
							{
								((CodeTypeOfExpression)expr).Type =
									new CodeTypeReference(
									CodeModifier.ReferenceType.GetStringElementType(field.Type));
							}
						}
					}
				}
			}
		}
		void RemoveDefaultValueAttribute(CodeTypeMember Member)
		{
			CodeAttributeDeclarationCollection attributes = 
				CodeSerializationAttribute.GetAttribute(
					Member, typeof(
						System.ComponentModel.DefaultValueAttribute));
			foreach(CodeAttributeDeclaration attribute in attributes)
			{
				CodeSerializationAttribute.RemoveAttribute(Member,
					attribute);
			}
		}
		// CHANGE: 2006-06-19:	  Version 0.56 Beta
		// Some problems with XmlTypeAttribute qualification.
		// Set true if required, else false.
		private bool QualifyXmlTypeAttribute
		{
			get { return true; }
		}
		#endregion Implementation
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			foreach(CodeTypeDeclaration type in 
				CodeGenerator.CodeNamespace.Types)
			{
				// Test if XmlRootAttribute'd elements are true
				// root elements. If not flag it by adding a
				// temporary attribute.
				CodeSerializationAttribute.FlagIfRoot(CodeGenerator, type);
				// Add/modify property serialization attributes and 
				// remove default value attributes.
				foreach(CodeTypeMember member in type.Members)
				{
					// Process fields only.
					if(member is CodeMemberField)
					{
						// Look to add the XmlElementAttribute for serializing
						// this field as a property.
						if(CodeXS.Schema.CodeGenerator.DefaultInstantiateObjects &&
							Property.CanInstantiate(CodeGenerator.GlobalCodeNamespace,
								((CodeMemberField)member).Type, member))
						{
							CodeSerializationAttribute.ModifySerializationAttribute(
								CodeGenerator.GlobalCodeNamespace, member);
						}
						// Fix the XmlArrayItemAttribute to a void the error:
						// 'The Form property may not be 'Unqualified' when an explicit 
						// Namespace property is present.'
						RemoveNamespaceAttributeWhenUnqualified(member,
							CodeGenerator.TargetXmlSchema.AttributeFormDefault);
						// Fix the typeof() setting for the XmlArrayItemAttribute to have a
						// rank one less than that of the field.
						// CHANGE: 2006-06-29:	 Version 0.57 Beta
						// Removed. No longer serializing collections.
						SetAttributeTypeOf(member);
					}
					this.RemoveDefaultValueAttribute(member);
				}
				// CHANGE: 2006-06-19:	  Version 0.56 Beta
				// Read property to execute XmlTypeAttribute qualification.
				if (this.QualifyXmlTypeAttribute)
				{
					// Set form qualifiers for XmlTypeAttribute'd types...
					CodeAttributeDeclarationCollection attributes =
						CodeSerializationAttribute.GetAttribute(
							type, typeof(XmlTypeAttribute));
					foreach (CodeAttributeDeclaration attribute in attributes)
					{
						CodeAttributeArgument nsArg =
							CodeSerializationAttribute.GetArgument(
							attribute, _nameSpaceArgument);
						this.SetTypeForm(type,
							CodeGenerator.TargetXmlSchema.ElementFormDefault,
							CodeGenerator.TargetXmlSchema.AttributeFormDefault,
							nsArg,
							CodeGenerator.TargetXmlSchema.TargetNamespace, true);
						foreach (CodeTypeMember member in type.Members)
						{
							// Process fields only.
							if (member is CodeMemberField)
							{
								this.SetTypeForm(member,
									CodeGenerator.TargetXmlSchema.ElementFormDefault,
									CodeGenerator.TargetXmlSchema.AttributeFormDefault,
									nsArg,
									CodeGenerator.TargetXmlSchema.TargetNamespace,
									true);
							}
						}
					}
				}
				// CHANGE: 2006-06-19:	  Version 0.56 Beta
				// Fix XmlChoiceIdentifierAttribute to add leading underscore to 
				// its name, and remove the XmlAnyElementAttribute attribute. 
				foreach (CodeTypeMember member in type.Members)
				{
					CodeSerializationAttribute.FixXmlChoiceIdentifierAttribute(member);
				}
				// CHANGE: 2006-06-19:	  Version 0.56 Beta
				// Remove the 
				// System.Xml.Serialization.XmlEnumAttribute("##any:") attribute.
				CodeSerializationAttribute.FixXmlEnumAttribute(type);
			}
		}
		public bool Enabled
		{
			get { return true; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion ICodeModifier Members
	}
	#endregion  2. SerializationAttributes
	#region  3. DeclarationCommentConstruct
	public class DeclarationCommentConstruct : ICodeModifier
	{
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			CodeGenerator.ReplaceableParameter(
				ReplaceParameterHandler.Parameters.__namespace__.ToString(),
				CodeGenerator.CodeNamespace.Name);
			foreach(CodeTypeDeclaration type in 
				CodeGenerator.CodeNamespace.Types)
			{
				string section = "Unhandled declaration";
				if(type.IsClass)
				{
					CodeGenerator.ReplaceableParameter(
						ReplaceParameterHandler.Parameters.__class__.ToString(),
						type.Name);
					section = Defaults.ClassTag + @"/" + "class";
				}
				if(type.IsEnum)
				{
					CodeGenerator.ReplaceableParameter(
						ReplaceParameterHandler.Parameters.__enum__.ToString(),
						type.Name);
					section = Defaults.EnumTag + @"/" + "enum";
				}
				ReplaceableParameter replace = new ReplaceableParameter(
					CodeGenerator, new ReplaceParameterHandler());
				Documentation document = new Documentation();
				document.CreateDocumentation(CodeGenerator, replace,
					type, section, false, false);
			}
		}
		public bool Enabled
		{
			get { return true; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion ICodeModifier Members
	}
	#endregion  3. DeclarationCommentConstruct
	#region  4. PropertyConstruct
	public class PropertyConstruct : ICodeModifier
	{
		#region Implementation
		static public string GetStringType(CodeMemberField Field)
		{
			return CodeModifier.ReferenceType.GetStringType(Field.Type);
		}
		private CodeTypeMember GetBaseDefinedMember(CodeNamespace Namespace,
			CodeTypeDeclaration Decl, CodeTypeMember Member,
			bool IgnoreCase)
		{
			CodeTypeMember member = null;
			// Look for existing property in base class
			foreach(CodeTypeReference baseType in Decl.BaseTypes)
			{
				foreach(CodeTypeDeclaration type in Namespace.Types)
				{
					if(type.IsClass && (baseType.BaseType == type.Name) )
					{
						member = this.GetBaseDefinedMember(Namespace,
							type, Member, IgnoreCase);
						if(member == null)
						{
							foreach(CodeTypeMember m in type.Members)
							{
								if( (m.Name == Member.Name) ||
									(IgnoreCase &&
									(m.Name.ToLower() == Member.Name.ToLower())) )
								{
									member = m;
									break;
								}
							}
						}
					}
				}
			}
			return member;
		}	
		private CodeMemberProperty CreatePropertyFromField(
			CodeNamespace GlobalNamespace,
			CodeNamespace Namespace,
			CodeTypeDeclaration Declarer,
			CodeMemberField Field,
			CodeMemberField SpecifiedField,
			bool DefaultInstantiate, bool UseScalarType)
		{
			string type = GetStringType(Field);
            if (SpecifiedField != null)
            {
               // type = type + "?";
            }
			// Create the property
			CodeMemberProperty property =
				Property.Create(GlobalNamespace, Namespace, Declarer, 
					Field.Name, type, Property.Accessor.Both, 
					MemberAttributes.Public | MemberAttributes.Final,
					Field, false,
					DefaultInstantiate, UseScalarType);
			// Set the property members
			property.LinePragma = Field.LinePragma;
			property.Comments.AddRange(Field.Comments);
			if(Field.UserData != null)
			{
				foreach(object key in Field.UserData.Keys)
				{
					property.UserData.Add(key, Field.UserData[key]);
				}
			}
			// Move custom attributes from the field to the property
			property.CustomAttributes.AddRange(Field.CustomAttributes);
            //andrey
            //if(SpecifiedField != null) 
            //{
            //    // Set the specified field when the set method is called
            //    property.SetStatements.Add(
            //        new CodeAssignStatement(
            //        new CodeFieldReferenceExpression(
            //        new CodeThisReferenceExpression(), 
            //        Field.Name + "Specified"), 
            //        new CodePrimitiveExpression(true)));
            //}
			return property;
		}
		private void InternalizeField(CodeMemberField Field)
		{
			// Make the field to private
			Field.Attributes = MemberAttributes.Private | MemberAttributes.Final;
			Field.CustomAttributes.Clear();
            if (GetStringType(Field) == "System.String")
            {
                Field.InitExpression = new CodePrimitiveExpression("");
            }
		}
		private string GetRenamedFieldName(CodeMemberField Field)
		{
			// CHANGE: 2006-06-19:	  Version 0.56 Beta
			// Problem with XSD using 'Id' and 'id'..
			//			string s = "m" + char.ToLower(Field.Name[0]) + Field.Name.Substring(1);
			//			return s;
			return "m" + Field.Name;
		}
		private CodeMemberProperty CreateCodeProperty(
			CodeGenerator CodeGenerator, CodeTypeDeclaration Type,
			CodeMemberField Field, CodeMemberField SpecifiedField,
			bool InstantiateObject, bool UseScalarType)
		{
			CodeMemberProperty property =
				this.CreatePropertyFromField(
					CodeGenerator.GlobalCodeNamespace,
					CodeGenerator.CodeNamespace, 
					Type, Field, SpecifiedField,
					InstantiateObject, UseScalarType);
			// Look for an existing property in base class
			CodeTypeMember baseMember = this.GetBaseDefinedMember(
				CodeGenerator.CodeNamespace, Type, property, true);
			// For VB, add new if the base property exists.
			if( (baseMember != null) &&
				(Schema.CodeGenerator.CodeLanguage == 
					Schema.CodeGenerator.Language.VB) )
			{
				property.Attributes |= MemberAttributes.New;
			}
			return property;
		}
		#endregion Implementation
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			const string specified = "Specified";
			// Setup replaceable parms for comments.
			CodeGenerator.ReplaceableParameter(
				ReplaceParameterHandler.Parameters.__namespace__.ToString(),
					CodeGenerator.CodeNamespace.Name);
			foreach(CodeTypeDeclaration type in 
				CodeGenerator.CodeNamespace.Types)
			{
				if(type.IsEnum) continue;
				ArrayList fields = new ArrayList();
				Hashtable fieldLookup = new Hashtable();
				foreach(CodeTypeMember member in type.Members)
				{
					if(member is CodeMemberField)
					{
						fields.Add(member);
						fieldLookup.Add(member.Name, member);
					}
				}
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__class__.ToString(),
					type.Name);
				// CHANGE: 2006-06-19:	  Version 0.56 Beta
				// Test if collections will also be applied.
				// TODO: Use code modifier types, rather than code modifier
				// names to determine this.
				bool usingCollections =
					CodeGenerator.IsCodeModifierEnabled(
						"CodeXS.Schema.StandardCodeModifier.CollectionConstruct");
				foreach(object entry in fields)
				{
					CodeMemberField field = entry as CodeMemberField;
                    if(field.Name.EndsWith(specified)) continue;
					bool textType = 
						(CodeModifier.CodeSerializationAttribute.GetAttribute(
							field, typeof(XmlTextAttribute)).Count > 0);
					field.UserData[ReplaceParameterHandler.UserData.TextType.ToString()] = 
						textType;
					CodeMemberField specifiedField = 
						(CodeMemberField)fieldLookup[field.Name + specified];
                    if (specifiedField != null)

                    {
                        type.Members.Remove(specifiedField);
                        fieldLookup.Remove(specifiedField.Name);
                        //fields.Remove(specifiedField);
                        if (GetStringType(field) != "string")
                        {
                            field.Type = new CodeTypeReference(GetStringType(field) + "?");
                        }
                        else
                        {
                            field.Type = new CodeTypeReference(GetStringType(field));
                        }
                    }
					string renamedFieldName = this.GetRenamedFieldName(field);
					// Documentation.
					CodeGenerator.ReplaceableParameter(
						ReplaceParameterHandler.Parameters.__field__.ToString(),
						renamedFieldName);
					Documentation document = null;
					ReplaceableParameter replace = null;
					// Check if field member can be instantiated.
					bool canInst = Property.CanInstantiate(
						CodeGenerator.GlobalCodeNamespace, field.Type, field);
					if(textType) canInst = false;
					// CHANGE: 2006-06-19:	  Version 0.56 Beta
					// Use scalar-types for serialization properties.
					bool useScalarType = false;
					if (canInst &&
						CodeXS.Schema.CodeGenerator.DefaultInstantiateObjects)
					{
						// Create the property for serialization access.
						if (field.Type.ArrayRank > 0) useScalarType = true;
						if (!usingCollections) useScalarType = false;
						CodeMemberProperty serProp = this.CreateCodeProperty(
							CodeGenerator, type, field, specifiedField, false,
							useScalarType);
						serProp.Name = "_" + serProp.Name;
						CodeGenerator.ReplaceableParameter(
							ReplaceParameterHandler.Parameters.__property__.ToString(),
								serProp.Name);
						document = new Documentation();
						replace = new ReplaceableParameter(
							CodeGenerator, new ReplaceParameterHandler());
						document.CreateDocumentation(CodeGenerator, replace,
							serProp, "class/sproperty", false, false);
						type.Members.Add(serProp);
					}
					useScalarType = false;
					// Create the property for normal access.
					CodeMemberProperty accProp = this.CreateCodeProperty(
						CodeGenerator, type, field, specifiedField, 
						(bool)(usingCollections ? canInst : false), 
						useScalarType);
					if (canInst &&
						CodeXS.Schema.CodeGenerator.DefaultInstantiateObjects)
					{
						// Clear all serialization attributes.
						accProp.CustomAttributes.Clear();
						// Fix/add the XmlIgnoreAttribute attribute.
						Property.AddIgnoreAttribute(
							CodeGenerator.GlobalCodeNamespace, accProp);
					}
					CodeGenerator.ReplaceableParameter(
						ReplaceParameterHandler.Parameters.__property__.ToString(),
							accProp.Name);
					document = new Documentation();
					replace = new ReplaceableParameter(
						CodeGenerator, new ReplaceParameterHandler());
					document.CreateDocumentation(CodeGenerator, replace,
						accProp, "class/property", false, false);
					this.InternalizeField(field);
					field.Name = renamedFieldName;
					document.CreateDocumentation(CodeGenerator, replace,
						field, "class/field", false, false);
					type.Members.Add(accProp);
				}
			}
		}
		public bool Enabled
		{
			get { return true; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion ICodeModifier Members
	}
	#endregion  4. PropertyConstruct
	#region  5. CollectionConstruct
	public class CollectionConstruct : ICodeModifier
	{
		#region Implementation
		private string GetValidClassName(CodeTypeReference ReferenceType)
		{
			// CHANGE: 2006-06-29:	  Version 0.57 Beta
			// Use CollectionBuilder.CollectionSuffixString.
			return LanguageBuilder.GetValidClassName(
				GetValidTypeName(ReferenceType, false) + 
				CollectionBuilder.CollectionSuffixString);
		}
		// CHANGE: 2006-06-29:	 Version 0.57 Beta
		// Moved 'GetValidTypeName to CollectionBuilder class.
		private string GetValidTypeName(CodeTypeReference ReferenceType, bool UseScalarType)	
		{
			return CollectionBuilder.GetValidTypeName(ReferenceType, UseScalarType);
		}
		private string GetElementTypeName(string CollectionName)
		{
			// CHANGE: 2006-06-29:	  Version 0.57 Beta
			// Use helper method:
			// CollectionBuilder.GetElementTypeName.
			string element = 
				CollectionBuilder.GetElementTypeName(CollectionName);
			element =
				CodeBuilder.LanguageBuilder.GetValidName(element);
			return element;
		}
		#endregion Implementation
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			CodeGenerator.ReplaceableParameter(
				ReplaceParameterHandler.Parameters.__namespace__.ToString(),
					CodeGenerator.CodeNamespace.Name);
			SortedList collections = new SortedList();
			foreach(CodeTypeDeclaration type in 
				CodeGenerator.CodeNamespace.Types)
			{
				if(type.IsEnum) continue;
				Hashtable fields = new Hashtable();
				foreach(CodeTypeMember member in type.Members)
				{
					if(member is CodeMemberField)
					{
						fields.Add(type.Name + "." + member.Name, member);
					}
				}
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__class__.ToString(),
					type.Name);
				foreach(DictionaryEntry entry in fields)
				{
					CodeMemberField field = entry.Value as CodeMemberField;
					CodeTypeReference elementType = field.Type.ArrayElementType;
					if(elementType == null) continue;
					Type baseElementType = Type.GetType(elementType.BaseType);
					if( (field.Type.ArrayElementType != null) &&
						(
							(baseElementType == null) ||
							(Property.IsPrimitive(baseElementType))
						) 
					  )
					{
						CodeTypeDeclaration decl = null;
						CodeTypeReference colRef = field.Type;
						string className = 
							this.GetValidClassName(colRef);
						string colClassName = className;
						// CHANGE: 2006-06-29:	 Version 0.57 Beta
						// Make this a lot simpler...
						while (CollectionBuilder.IsCollectionType(colClassName,
							CollectionBuilder.GetValidTypeName(colRef, true)))
						{
							if(collections.ContainsKey(colClassName))
							{
								decl = (CodeTypeDeclaration)
									collections[colClassName];
							}
							else
							{
								string classType = 
									this.GetValidTypeName(colRef, true);
								CollectionBuilder builder = 
									new CollectionBuilder(
									CodeGenerator.CodeNamespace.Name, 
									colClassName, classType, colRef);
								CodeTypeDeclarationCollection decls = 
									builder.ToCodeDom();
								decl = decls[0];
								decl.Name = colClassName;
								decl.UserData[ReplaceParameterHandler.UserData.ScalarType.ToString()] = 
									colRef.BaseType;
								collections.Add(decl.Name, decl);
							}
							colClassName = CollectionBuilder.GetElementTypeName(colClassName);
							if(colRef != null)
							{
								colRef = colRef.ArrayElementType;
							}
						}
						bool textType = false;
						object o = 
							field.UserData[ReplaceParameterHandler.UserData.TextType.ToString()];
						if(o != null) textType = (bool)o;
						if( ! textType)
						{
							field.Type.ArrayElementType = null;
							field.Type.ArrayRank = 0;
							//field.Type.BaseType = decl.Name;
							field.Type.BaseType = className;
						}
					}
				}
			}
			foreach(object entry in collections.Values)
			{
				CodeTypeDeclaration decl = entry as CodeTypeDeclaration;
				Documentation documentation = new Documentation();
				string arrayObject = "object";
				string anArrayOf = string.Empty;
				string collectionType = decl.Name;
				string elementType =
					decl.UserData[ReplaceParameterHandler.UserData.ScalarType.ToString()].
						ToString();
				string elementName = LanguageBuilder.RemoveStandardPrefixes(elementType);
				// CHANGE: 2006-06-29:	  Version 0.57 Beta
				// Change test actual collection.
				if (CollectionBuilder.IsCollectionType(collectionType,
					elementType))
				{
					anArrayOf = "an array of";
					arrayObject = "object";
				}
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__array_object__.ToString(),
						arrayObject);
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__an_array_of__.ToString(),
						anArrayOf);
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__array_element_type__.ToString(),
						elementType);
				string elementTypeName =
					CollectionBuilder.GetElementTypeName(collectionType);
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__element_type__.ToString(),
					elementTypeName);
				if(CollectionBuilder.IsCollectionType(elementTypeName,
					elementName.Replace(ArrayType.ArrayParentheses, string.Empty)))
				{
					elementName = elementTypeName;
				}				
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__element_name__.ToString(),
					elementName);
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__collection_type__.ToString(),
					collectionType);
				documentation.CreateDocumentation(CodeGenerator, 
					new ReplaceableParameter(
						CodeGenerator, new ReplaceParameterHandler()),
					decl, "collection", true, true);
				CodeGenerator.CodeNamespace.Types.Add(decl);
			}
		}
		public bool Enabled
		{
			get { return true; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion
	}
	#endregion  5. CollectionConstruct
	#region  6. SerializationSupportConstruct
	public class SerializationSupportConstruct : ICodeModifier
	{
		#region Implementation
		// CHANGE: 2006-06-29:	 Version 0.57 Beta
		// Moved Schema.CodeModifer.Member and made 'public static'
		#endregion Implementation
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			CodeTypeDeclarationCollection decls = null;
			CodeGenerator.ReplaceableParameter(
				ReplaceParameterHandler.Parameters.__namespace__.ToString(),
				CodeGenerator.CodeNamespace.Name);
			foreach(CodeTypeDeclaration type in CodeGenerator.CodeNamespace.Types)
			{
				if(type.IsEnum) continue;
				string classType = type.Name;
				CodeGenerator.ReplaceableParameter(
					ReplaceParameterHandler.Parameters.__class__.ToString(),
					type.Name);
				Documentation document = new Documentation();
				ReplaceableParameter replace = new ReplaceableParameter(
					CodeGenerator, new ReplaceParameterHandler());
				SerializationSupportBuilder supportBuilder = 
					new SerializationSupportBuilder(
						CodeGenerator.CodeNamespace.Name, classType, classType);
				decls = supportBuilder.ToCodeDom();
				if(decls.Count > 0)
				{
					foreach(CodeTypeMember member in decls[0].Members)
					{
						MemberAttributes scope = 
							member.Attributes & MemberAttributes.ScopeMask;
						if(scope == MemberAttributes.Static)
						{
							if(Member.UseOverrideModifier(
								CodeGenerator.GlobalCodeNamespace, type))
							{
								member.Attributes |= MemberAttributes.New;
							}
						}
						else
						{
							if(Member.UseOverrideModifier(
								CodeGenerator.GlobalCodeNamespace, type))
							{
								member.Attributes |= MemberAttributes.Override;
							}
						}
						type.Members.Add(member);
						// Add the documentation comments
						document.CreateDocumentation(CodeGenerator, replace,
							member, "class/" + member.Name, false, false);
					}
				}
			}
		}
		public bool Enabled
		{
			get { return true; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion ICodeModifier Members
	}
	#endregion  6. SerializationSupportConstruct
	#region  7. SchemaLocationAttribute
	public class SchemaLocationAttribute : ICodeModifier
	{
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			foreach(CodeTypeDeclaration type in 
				CodeGenerator.CodeNamespace.Types)
			{
				// Add the schemaLocation attribute to the root element.
				CodeAttributeDeclarationCollection attributes = 
					CodeSerializationAttribute.GetAttribute(
					type, typeof(XmlRootAttribute));
				// Assume only one...
				if( (attributes.Count > 0) &&
					( ! CodeSerializationAttribute.IsReferenced(type)) )
				{
					CodeAttributeArgument namespaceArg = 
						CodeSerializationAttribute.GetArgument(attributes[0], 
						"Namespace");
					string nameSpace =
						CodeSerializationAttribute.GetArgumentValue(namespaceArg);
					CodeMemberProperty property = new CodeMemberProperty();
					property.Type = new CodeTypeReference(typeof(string));
					property.Name = "schemaLocation";
					if( (nameSpace == null) || (nameSpace == "") )
					{
						property.Name = "noNamespaceSchemaLocation";
					}
					property.Attributes = MemberAttributes.Public;
					property.HasGet = true;
					property.HasSet = true;
					string location = 
						CodeGenerator.TargetXmlSchema.TargetNamespace + " " +
						CodeGenerator.TargetXmlSchema.SourceUri;
					location = location.Trim();
					property.GetStatements.Add(
						new CodeMethodReturnStatement(
						new CodePrimitiveExpression(location)));
					CodeAttributeDeclaration schemaLocation =
						new CodeAttributeDeclaration(
						"System.Xml.Serialization.XmlAttributeAttribute",
						new CodeAttributeArgument[]
							{
								new CodeAttributeArgument("Namespace", 
								new CodePrimitiveExpression(
								"http://www.w3.org/2001/XMLSchema-instance"))
							} );
					CodeSerializationAttribute.SetAttribute(property,
						schemaLocation, -1);
					Documentation document = new Documentation();
					ReplaceableParameter replace = new ReplaceableParameter(
						CodeGenerator, new ReplaceParameterHandler());
					document.CreateDocumentation(CodeGenerator, replace,
						property, "class/schemaLocation", false, false);
					type.Members.Add(property);
				}
			}
			Member.SetOverrideModifiers(CodeGenerator.CodeNamespace);
		}
		public bool Enabled
		{
			get { return false; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion ICodeModifier Members
	}
	#endregion  7. SchemaLocationAttribute
	#region  8. LanguageFixupConstruct
	public class LanguageFixupConstruct : ICodeModifier
	{
		#region Implementation
		static private string[,] _vbReplace = new string[,]
		{
			{ "wend", null },
			{ "on", null }
		};
		private string VBLangFixup(string Decl)
		{
			string s = Decl;
			for(int i = 0; i < _vbReplace.GetLength(0); i++)
			{
				if(Decl.ToLower() == _vbReplace[i,0])
				{
					s = (_vbReplace[i,1] != null) ? 
						_vbReplace[i,1] : "_" + _vbReplace[i,0];
					s = s.Replace(_vbReplace[i,0], Decl);
				}
			}
			return s;
		}
		#endregion Implementation
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			foreach(CodeTypeDeclaration type in 
				CodeGenerator.CodeNamespace.Types)
			{
				// Fix enums for now..
				if( ! type.IsEnum) continue;
				if(Schema.CodeGenerator.CodeLanguage ==
					Schema.CodeGenerator.Language.VB)
				{
					foreach(CodeTypeMember m in type.Members)
					{
						m.Name = this.VBLangFixup(m.Name);
					}
				}
			}
		}
		public bool Enabled
		{
			get { return true; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion ICodeModifier Members
	}
	#endregion  8. PropertyConstruct
	#region  9. SerializationClassConstruct
	public class SerializationClassConstruct : ICodeModifier
	{
		#region Implementation
		private string[] GetSchemaNamespaces(CodeGenerator CodeGenerator)
		{
			ArrayList schemas = new ArrayList();
			XmlSchema schema = CodeGenerator.TargetXmlSchema;
			XmlQualifiedName[] namespaces = schema.Namespaces.ToArray();
			const string xsi = "http://www.w3.org/2001/XMLSchema-instance";
			string xsiDescriptor = "\"xsi\",\"" + xsi + "\"";
			string descriptor = null;
			bool hasXsiDescriptor = false;
			foreach(XmlQualifiedName ns in namespaces)
			{
				descriptor = "\"" + ns.Name + "\",\"" + ns.Namespace + "\"";
				if(ns.Namespace == schema.TargetNamespace)
				{
					schemas.Insert(0, descriptor);
				}
				else
				{
					schemas.Add(descriptor);
				}
				if(descriptor.ToLower() == xsiDescriptor.ToLower())
					hasXsiDescriptor = true;
			}
			if( ! hasXsiDescriptor)
			{
				schemas.Add(xsiDescriptor);
			}
			return (string[])schemas.ToArray(typeof(string));
		}
		#endregion Implementation
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			const string uriPath = "local:serializer";
			const string fileName = "Serializer";
			if(CodeGenerator.GetCodeFilePath(uriPath) == null)
			{
				// Create empty namespace from target namespace.
				CodeNamespace ns = 
					CodeGenerator.CreateNamespaceFromTarget(true);
				SerializationClassBuilder classBuilder = 
					new SerializationClassBuilder(
					CodeGenerator.CodeNamespace.Name, null, null,
					this.GetSchemaNamespaces(CodeGenerator));
				CodeTypeDeclarationCollection decls = 
					classBuilder.ToCodeDom();
				if(decls.Count > 0)
				{
					CodeTypeDeclaration decl = decls[0];
					ns.Types.Add(decl);
					const string ppName = "PrettyPrint";
					CodeMemberField ppField = 
						new CodeMemberField(typeof(bool), ppName);
					ppField.Attributes = 
						MemberAttributes.Private | MemberAttributes.Static;
					decl.Members.Add(ppField);
					CodeMemberProperty ppProp = Property.Create(
						CodeGenerator.GlobalCodeNamespace,
						CodeGenerator.CodeNamespace,
						decl, ppName,
						"System.Boolean", Property.Accessor.Both,
						MemberAttributes.Public | MemberAttributes.Static,
						ppField, true,
						CodeXS.Schema.CodeGenerator.DefaultInstantiateObjects,
						false);
					decl.Members.Add(ppProp);
					Documentation documentation = new Documentation();
					documentation.CreateDocumentation(CodeGenerator, 
						new ReplaceableParameter(
						CodeGenerator, new ReplaceParameterHandler()),
						decl, "serializer", true, true);
				}
				ICodeModifier[] modifiers = new ICodeModifier[]
				{
					new HeadComment() as ICodeModifier,
					new ReferencesConstruct() as ICodeModifier,
					new LanguageFixupConstruct() as ICodeModifier,
				};
				CodeGenerator.GenerateCode(uriPath,
					fileName, ns, modifiers);
			}
		}
		public bool Enabled
		{
			get { return false; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion ICodeModifier Members
	}
	#endregion  9. SerializationClassConstruct
	#region 10. CommonCodeClassConstruct
	public class CommonCodeClassConstruct : ICodeModifier
	{
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			CodeNamespace common = CodeGenerator.CommonCodeNamespace;
			if(common.Types.Count > 0)
			{
				const string uriPath = "local:common";
				string fileName = 
					CodeGenerator.CodeNamespaceName + "_Common";
				ICodeModifier[] modifiers = new ICodeModifier[]
				{
					new HeadComment() as ICodeModifier,
					new ReferencesConstruct() as ICodeModifier,
				};
				CodeGenerator.GenerateCode(uriPath,
					fileName, common, modifiers);
			}
		}
		public bool Enabled
		{
			get { return true; }
		}
		public bool RunOnce
		{
			get { return true; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion ICodeModifier Members
	}
	#endregion 10. CommonCodeClassConstruct
	#region 11. CodeAttributeCleanup
	public class CodeAttributeCleanup : ICodeModifier
	{
		#region ICodeModifier Members
		public string Name
		{
			get { return Schema.Defaults.ModifierName(this); }
		}
		public string Version
		{
			get { return Schema.Information.Version(); }
		}
		public void Execute(CodeGenerator CodeGenerator)
		{
			foreach(CodeTypeDeclaration type in 
				CodeGenerator.CodeNamespace.Types)
			{
				CodeSerializationAttribute.RemoveReferenced(type);
			}
		}
		public bool Enabled
		{
			get { return true; }
		}
		public bool RunOnce
		{
			get { return false; }
		}
		public void Initialize(CodeGenerator CodeGenerator) { }
		public void Deinitialize(CodeGenerator CodeGenerator) { }
		public object SchemaImporterExtension
		{ get { return null; } }
		#endregion ICodeModifier Members
	}
	#endregion 11. CodeAttributeCleanup
	#region IReplaceParameter Implementation
	public class ReplaceParameterHandler : IReplaceParameter
	{
		#region Implementation
		public enum Parameters
		{
			__array_element_type__,
			__class__,
			__collection_type__,
			__element_name__,
			__element_type__,
			__enum__,
			__field__,
			__namespace__,
			__property__,
			__array_object__,
			__an_array_of__,
		}
		public enum UserData
		{
			ScalarType,
			TextType,
		}
		#endregion Implementation
		#region IReplaceParameter Members
		public string Replace(CodeGenerator CodeGenerator, string Name, object Value)
		{
			string replace = Name;
			replace = Value.ToString();
			return replace;
		}
		public bool Enabled
		{
			get { return true; }
		}
		#endregion
	}
	#endregion IReplaceParameter Implementation
}
